﻿
package extremefx.collections {
	import extremefx.conditions.requiresString;	
	import extremefx.ICloneable;
	import extremefx.IEnumerator;
	
	import flash.net.registerClassAlias;
	import flash.utils.IDataInput;
	import flash.utils.IDataOutput;
	import flash.utils.Proxy;

	/**
	 * @author Marcelo Volmaro
	 * Represents a collection of keys and values.
	 */
	 
	public final class Dictionary extends Proxy implements IDictionary, ICloneable, ICollection {
		registerClassAlias("extremefx.collections.Dictionary", Dictionary);
		
		private var _dict:Array;
		private var _keysArray:Vector.<String>;
		private var _valuesArray:Array;
		internal var _count:uint;
		
		public function Dictionary(pDic:IDictionary = null) {
			clear();
			
			if (pDic is IDictionary){
				var e:IEnumerator = getEnumerator();
				while(e.moveNext()) {
					add(e.current.key, e.current.value);
				}
			}
		}

		public function add(pKey:String, pValue:*):void {
			requiresString(pKey, "key").isNotNullOrEmpty();
			
			var idx:int = _keysArray.indexOf(pKey);
			if (pValue == null) pValue = "$$EFX$$NULL$$";

			if (idx == -1){
				_count = _keysArray.push(pKey);
				_valuesArray.push(pValue);
				
			} else {
				_keysArray[idx] = pKey;
				_valuesArray[idx] = pValue;
			}
			
			_dict[pKey] = pValue;
		}
		
		public function clear():void {
			_dict = new Array();
			_keysArray = new Vector.<String>();
			_valuesArray = [];
			_count = 0;
		}
		
		public function contains(pKey:String):Boolean {
			requiresString(pKey, "key").isNotNullOrEmpty();
			return (-1 < _keysArray.indexOf(pKey));
		}
		
		public function containsKey(pKey:String):Boolean {
			requiresString(pKey, "key").isNotNullOrEmpty();
			return (-1 < _keysArray.indexOf(pKey));
		}
		
		public function containsValue(pValue:*):Boolean {
			if (pValue == null) pValue = "$$EFXNULL$$";
			return (-1 < _valuesArray.indexOf(pValue));
		}
		
		public function remove(pKey:String):void {
			requiresString(pKey, "key").isNotNullOrEmpty();
			
			var idx:uint = _keysArray.indexOf(pKey);
			if (idx != -1){
				_keysArray.splice(idx, 1);
				_valuesArray.splice(idx, 1);
				_count = _keysArray.length;
				_dict[pKey] = null;
				delete _dict[pKey]; 
			}
		}
		
		public function toArray():Array {
			var a:Array = new Array(), key:String;
			
			for (var i:uint = 0; i<_count; i++){
				a[i++] = new KeyValuePair(key = _keysArray[i], _dict[key]);
			}
			
			return a;
		}
		
		public function getEnumerator():IEnumerator {
			return new DictionaryEnumerator(_keysArray, _dict);
		}
		
		public function readExternal(input:IDataInput):void {
			_dict = input.readObject();
			_keysArray = Vector.<String>(input.readObject());
			_valuesArray = input.readObject() as Array;
			_count = input.readUnsignedInt();
		}
		
		public function writeExternal(output:IDataOutput):void {
			output.writeObject(_dict);
			output.writeObject(_keysArray);
			output.writeObject(_valuesArray);
			output.writeUnsignedInt(_count);
		}
		
		public function get keys():Vector.<String> {
			return _keysArray.concat();
		}
		
		public function get values():Array {
			return _valuesArray.concat();
		}
		
		public function get count():uint {
			return _count;
		}
		
		public function clone():Object {
			return new Dictionary(this);
		}
	}
}

import extremefx.IEnumerator;
import extremefx.collections.KeyValuePair;

final class DictionaryEnumerator implements IEnumerator {
	private var _dict:Array;
	private var _keys:Vector.<String>;
	private var _index:int;
	private var _count:uint;
	
	public function DictionaryEnumerator(pKeys:Vector.<String>, pDict:Array) {
		_keys = pKeys;
		_dict = pDict;
		_index = -1;
		_count = pKeys.length;
	}

	public function moveNext():Boolean {
		_index++;
		return (_index < _count);
	}
	
	public function reset():void {
		_index = -1;
	}
	
	public function get current():* {
		var key:String;
		return new KeyValuePair(key = _keys[_index], _dict[key]);
	}
}